package sip

import (
	"net/url"
	"net"
	"strings"
	"strconv"
	)

type SipUrl struct {
	Scheme   string
        Host     string
	User     string
        Path     string
        RawQuery string // encoded query values, without '?'
        Fragment string // fragment for references, without '#'

}

type MediaDescriptor interface{
}

type Transaction struct { // a request response set
	Method string
	Seq int
	Reference *UA
	RDialog *Dialog
}

type Dialog struct {  // a complete call o registration
	CallId string
	FromTag string
	ToTag string
	To string
	Reference *UA
	Transactions map[string]*Transaction
}

type UA struct {
	Identity string
	Line string
	Ip net.IP
	Host string
	Domain string
	UserAgent string
	Contact *SipUrl
	MaxForward int
	Dialogs map[string]*Dialog
	MediaCapability map[string]*MediaDescriptor
}

func NewUA(name, line, local_ip, domain string) (*UA, error) {
	p := net.ParseIP(local_ip)
	if p==nil {
		return nil, nil // error here
	}
	c := &SipUrl{"sip", local_ip, name, "", "", ""}
	n := &UA{
		Identity:         name, 
		Line:	          line, 
		Ip:               p, 
		UserAgent:        "GoSipAgent",
		Host:             local_ip,
		Domain:           domain,
		Contact:          c,
		MaxForward:       70, 
		Dialogs:          make(map[string]*Dialog),
		MediaCapability:  make(map[string]*MediaDescriptor),
	}
	return n, nil
}

func (x *UA)GetContact() string {
	return "<" + x.Contact.GetUrl().String() + ">"
}

func (x *UA)GetName() string {
	return "<" + x.Identity+"@"+ x.Domain + ">"
}

func (x *UA)NewDialog(to string) *Dialog {
	t := to
	if ! strings.Contains(to, "@") {
		t = to + "@" + x.Domain
	}
	tmp := &Dialog{
		CallId: GenerateCallid("@"+x.Host),
		FromTag: "",
		ToTag: "",
		To: t,
		Reference: x,
		Transactions: make(map[string]*Transaction),
	}
	x.Dialogs[tmp.CallId]= tmp
	return tmp
}

func (d *Dialog)NewTransaction(method string) *Transaction {
	tmp := &Transaction{
		Method: method,
		Seq: 0,
		Reference: d.Reference,
		RDialog: d,
	}
	d.Transactions[tmp.Method] = tmp
	return tmp
}

func (t *Transaction)GenerateRequest() (*Request, error) {
	t.Seq += 1
	in, _  := NewRequest(t.Method, t.RDialog.To, nil)
	in.Header.Add("to", t.RDialog.To)
	in.Header.Add("from", t.Reference.GetName())
	in.Header.Add("Max-Forwards", strconv.Itoa(t.Reference.MaxForward))
	in.Header.Add("User-Agent", t.Reference.UserAgent)
	in.Header.Add("Call-ID", t.RDialog.CallId)
	in.Header.Add("CSec", strconv.Itoa(t.Seq)+" " + t.Method)
	in.Header.Add("Contact", t.Reference.GetContact())
	// need to fix here
	if len(t.Reference.MediaCapability)!=0 {
	 	//add media capability to the request
	} else {
	 	in.Header.Add("Content-Length", "0")
	}
	return in, nil
}

func (x *UA)NewCall(to string) *Request{
	d := x.NewDialog(to)
	t := d.NewTransaction("INVITE")
	in, err:= t.GenerateRequest()
	if err!=nil {
		return nil
	}
	return in
}

func (x *UA)GetDialog(r *Request) *Dialog {
	if cid, ok := x.Dialogs[r.Header.Get("Call-ID")]; ok {
		return cid
	}
	return nil
}

func (x *UA)GetTransaction(r *Request) *Transaction {
	if cid := x.GetDialog(r); cid != nil {
		if tr, ok := cid.Transactions[r.Method]; ok {
			return tr
		}
	}
	return nil
}

func (u *SipUrl)GetUrl() *url.URL {
	opaque := strings.Join([]string{u.User, u.Host}, "@")
	a := &url.URL{u.Scheme, opaque, nil, "", "", "", ""}
	return a
}